package myJs.user.soft.preprocess

import com.softwaremill.quicklens._
import japgolly.scalajs.react.facade.SyntheticDragEvent
import myJs.Implicits._
import myJs.Utils.layer
import myJs.api.Api
import myJs.cps._
import myJs.myPkg._
import myJs.myPkg.reactHookForm._
import myJs.tool.Tool
import myJs.tool.Tool.messages
import myJs.user.workflow.featureSelection.AllFSSoft
import org.scalajs.dom.{html, window}
import org.scalajs.macrotaskexecutor.MacrotaskExecutor.Implicits._
import shared.Pojo.{EachPreprocessData, MetDataKind, PreprocessSoftData}
import shared.SharedTool
import shared.pojo.Data.{GetNumericColNames, GetRowCols}
import shared.pojo.Pojo
import shared.pojo.Pojo.workflowMission._
import shared.tool.DefaultPickle

import scala.concurrent.Future
import scala.scalajs.js
import scala.scalajs.js.Dynamic.{global => g}
import scala.scalajs.js.JSON
import upickle.default._

import japgolly.scalajs.react.vdom.all._
import japgolly.scalajs.react._
import japgolly.scalajs.react.React.Fragment
import japgolly.scalajs.react.util.EffectCatsEffect._
import myJs.myPkg.Hooks._
import cats.effect._
import myJs.Implicits._
import myJs.Utils._
import cats.syntax.all._

/** Created by yz on 3/11/2020
  */
object Cp {

  case class Props(missionName: String) {
    @inline def render: VdomElement = {
      Component(this)
    }
  }

  val curTool = Preprocess

  val Component = ScalaFnComponent[Props] { props =>
    val useForm = ReactHookForm.useForm(UseFormOptions(mode = "onChange", resolver = yupResolver(Schema.schema)))

    val (softKind, setSoftKind) = useState("preprocess")
    val (myData, setMyData) = useState(PreprocessSoftData(missionName = props.missionName))
    val (extraData, setExtraData) = useState(ExtraData())

    val useFieldArray = ReactHookForm.useFieldArray(
      UseFieldArrayOptions(
        control = useForm.control,
        name = s"datas"
      )
    )

    object FTool {

      def myDataChange = (newData: PreprocessSoftData) => {
        setMyData(newData)
      }

      def fileSelectShowChange = (v: Boolean) => {
        val newData = extraData.modify(_.fileSelectShow).setTo(v)
        setExtraData(newData)
      }

      def extraDataChange = (v: ExtraData) => {
        setExtraData(v)
      }

      def fileSelect = (v: String) =>
        {
          FTool.fileSelectShowChange(false) >>
            FTool.fileChange(v)
        }.unsafeRunSync()

      def groupFileSelect = (v: String) =>
        {
          FTool.fileSelectShowChange(false) >>
            FTool.groupFileChange(v).map { x =>
              useForm.setValue("groupFile", v)
            }
        }.unsafeRunSync()

      def fileSelectShow = {
        val newData = extraData
          .modify(_.tabs)
          .setTo(List(MetDataKind.name, "example"))
          .modify(_.fileSelectShow)
          .setTo(true)
          .modify(_.exampleFileName)
          .setTo("data.txt")
          .modify(_.fileSelect)
          .setTo(FTool.fileSelect)
        setExtraData(newData)
      }

      def groupFileSelectShow = {
        if (myData.file.isEmpty) SyncIO {
          useForm.trigger("file")
        }
        else {
          val newRequest = Pojo.groupFileData.GetAll.Request(id = curTool.getId, fileName = myData.file)
          val newData = extraData
            .modify(_.tabs)
            .setTo(List("group", "example"))
            .modify(_.request)
            .setTo(newRequest)
            .modify(_.fileSelectShow)
            .setTo(true)
            .modify(_.exampleFileName)
            .setTo("group.txt")
            .modify(_.fileSelect)
            .setTo(FTool.groupFileSelect)
          setExtraData(newData)
        }
      }

      def refreshExtraData = {
        (FTool.getRowNames, FTool.getGroupHeaders).tupled.flatMapSync { case (rowNames, headers) =>
          val newExtraData = extraData
            .modify(_.rowNames)
            .setTo(rowNames)
            .modify(_.groupHeaders)
            .setTo(headers)
          setExtraData(newExtraData)
        }
      }

      def getRowNames = {
        if (myData.file.nonBlank) {
          val request = GetRowCols.Request(fileName = myData.file)
          Api.data.getRowCols(request).map { response =>
            response.rowNames
          }
        } else {
          IO {
            List[String]()
          }
        }

      }

      def getGroupHeaders = {
        if (myData.file.nonBlank && myData.groupFile.nonBlank) {
          val request = GetNumericColNames.Request(fileName = myData.groupFile)
          Api.data.getNumericColNames(request).map { response =>
            response.headers.filterNot { x =>
              List("Sample", "ClassNote", "Pair_ID").contains(x)
            }
          }
        } else {
          IO {
            List[String]()
          }
        }

      }

      def missionNameChange = (v: String) => {
        val newData = myData.modify(_.missionName).setTo(v)
        setMyData(newData)
      }

      def fileChange = (v: String) => {
        val newData = myData.modify(_.file).setTo(v)
        setMyData(newData)
      }

      def hasGroupChange = (v: Boolean) => {
        if (v && myData.file.isBlank) {
          layer.msg(messages("pleaseFirstSelectDataMatrix"), LayerOptions.icon(5).time(3000))
          useForm.trigger("file")
          val newData = myData.modify(_.hasGroup).setTo(false)
          setMyData(newData)
        } else {
          val newData = myData.modify(_.hasGroup).setTo(v)
          setMyData(newData)
        }
      }

      def groupFileChange = (v: String) => {
        val newData = myData.modify(_.groupFile).setTo(v)
        setMyData(newData)
      }

      def datasChange = (vs: List[EachPreprocessData]) => {
        val newData = myData.modify(_.datas).setTo(vs)
        setMyData(newData)
      }

      def addModule(moduleName: String) = {
        SyncIO {} >> {
          if (myData.file.isBlank) SyncIO {
            layer.msg(messages("pleaseFirstSelectDataMatrix"), LayerOptions.icon(5).time(3000))
          }
          else {
            val hasGroup = myData.hasGroup && myData.groupFile.nonBlank
            val c = AllCp.kindCpMap(moduleName)
            if (c == ExtraNormalCp && (!hasGroup)) SyncIO {
              layer.msg(
                messages("ExternalStandardNormalizationRequiresGroupingInformation"),
                LayerOptions.icon(5).time(3000)
              )
            }
            else if (c == ExtraNormalCp && (extraData.groupHeaders.isEmpty)) SyncIO {
              layer.msg(messages("noAdditionalDataColumnsForGroupingInformation"), LayerOptions.icon(5).time(3000))
            }
            else {
              val newData = EachPreprocessData(
                kind = moduleName,
                rowName = extraData.rowNames.head,
                extraColumn = extraData.groupHeaders.headOption.getOrElse("")
              )
              val newPreProcess = myData.modify(_.datas).using(_ :+ newData)
              useFieldArray.append(newData)
              setMyData(newPreProcess)
            }
          }
        }
      }.void

      def inputSelect = (inputE: html.Input) => {
        inputE.select()
      }

      def insertModule(curI: Int) = (moduleName: String) =>
        {
          val c = AllCp.kindCpMap(moduleName)
          if (c == ExtraNormalCp && extraData.groupHeaders.isEmpty) SyncIO {
            layer.msg(messages("noAdditionalDataColumnsForGroupingInformation"), LayerOptions.icon(5).time(3000))
          }
          else {
            val newData = EachPreprocessData(
              kind = moduleName,
              rowName = extraData.rowNames.head,
              extraColumn = extraData.groupHeaders.headOption.getOrElse("")
            )
            val newMyData = myData.modify(_.datas).using(_.insert(curI, newData))
            useFieldArray.insert(curI, newData)
            setMyData(newMyData)
          }
        }.void

      def removeModule = (curI: Int) => {
        SyncIO {} >> {
          val newMyData = myData.modify(_.datas).using { x =>
            x.removed(curI)
          }
          useFieldArray.remove(curI)
          setMyData(newMyData)
        }
      }

      def softNumCheck = {
        val size = myData.datas.size
        if (size == 0) {
          layer.msg(messages("PleaseAddAtLeastOneModule"), LayerOptions.icon(5).time(3000))
        }
        size > 0
      }

      def softKindCheck = {
        val hasGroup = myData.hasGroup && myData.groupFile.nonBlank
        val kinds = myData.datas.map(_.kind)
        if (kinds.contains(LoessCp.nameStr) && !hasGroup) {
          layer.msg(messages("CorrectionRequiresGroupingInformation"), LayerOptions.icon(5).time(3000))
          false
        } else if (kinds.contains(ExtraNormalCp.nameStr) && !hasGroup) {
          layer.msg(
            messages("ExternalStandardNormalizationRequiresGroupingInformation"),
            LayerOptions.icon(5).time(3000)
          )
          false
        } else true
      }

      def myRun = {
        if (softNumCheck && softKindCheck) {
          val layerOptions = LayerOptions.title(Tool.zhInfo).closeBtn(0).skin("layui-layer-molv").btn(js.Array())
          val index = layer.alert(Tool.myElement, layerOptions)
          val request = DefaultPickle.write(myData)
          Api.soft.analysis(request).map { response =>
            layer.close(index)
            if (response.valid) {
              window.location.href = g.jsRoutes.controllers.MissionController.missionManageBefore().url.toString
            } else {
              g.swal("Error", response.message, "error")
            }
          }
        }
        ()
      }

    }

    useEffect(
      {
        FTool.refreshExtraData
      },
      List(myData.file, myData.groupFile)
    )

    useEffect(
      {
        IO {} >> {
          val missionId = Tool.getMissionId
          if (missionId.nonBlank) {
            Api.mission.getConfigData(missionId).flatMapSync { response =>
              val responseData = read[PreprocessSoftData](response)
              val curMyData = responseData.modify(_.missionName).setTo(curTool.getMissionName)
              setMyData(curMyData) >> SyncIO {
                useForm.reset(curMyData)
              }

            }
          } else IO.unit
        }
      },
      List()
    )

    useForm.formState.errors.foreach { case (k, v) =>
      println(k, JSON.stringify(v))
    }

    div(
      FormProvider(
        trigger = useForm.trigger _,
        control = useForm.control,
        formState = useForm.formState,
        setValue = useForm.setValue _,
        watch = useForm.watch _,
        getFieldState = useForm.getFieldState _
      )(
        form(
          className := "form-horizontal",
          marginTop := 30.px,
          onSubmit := {
            useForm.handleSubmit((data: js.Dictionary[js.Any]) => {
              println(JSON.stringify(data))
              FTool.myRun
              ()
            })
          },
          InputInlineCp
            .Props(
              name = "missionName",
              label = messages("taskName"),
              onChange = FTool.missionNameChange,
              value = myData.missionName,
              onFocus = FTool.inputSelect
            )
            .render,
          FileSelectInlineCp
            .Props(
              fileSelectShow = FTool.fileSelectShow,
              label = messages("dataMatrix"),
              name = "file",
              value = myData.file,
              onChange = FTool.fileChange
            )
            .render,
          ToggleInlineCp
            .Props(
              value = myData.hasGroup,
              label = messages("importGroupInformation"),
              name = s"hasGroup",
              valueChange = FTool.hasGroupChange
            )
            .render,
          FileSelectInlineCp
            .Props(
              fileSelectShow = FTool.groupFileSelectShow,
              label = messages("groupFile"),
              name = "groupFile",
              value = myData.groupFile,
              onChange = FTool.groupFileChange,
              show = myData.hasGroup
            )
            .render,
          div(
            id := "preDeal",
            h4(s"${messages("processSteps")}"),
            useFieldArray.fields.toList.zipWithIndex.toVdomArray { case (item, curI) =>
              val data = myData.datas(curI)
              val c = AllCp.kindCpMap(data.kind)
              div(
                key := s"${item.id}",
                c.cp(
                  curI = curI,
                  datas = myData.datas,
                  datasChange = FTool.datasChange,
                  insertModule = FTool.insertModule(curI),
                  removeModule = FTool.removeModule,
                  extraData = extraData
                ),
                if (curI < useFieldArray.fields.size - 1) {
                  div(
                    textAlign.center,
                    marginTop := 10.px,
                    fontSize := 16.px,
                    className := "myArrow",
                    i(className := "fa fa-angle-double-down")
                  )
                } else EmptyVdom
              )
            },
            div(
              marginTop := 10.px,
              className := "form-group",
              div(
                className := "col-sm-offset-2 col-sm-2 btn-group",
                button(
                  `type` := "button",
                  className := "btn btn-default dropdown-toggle",
                  dataToggle := "dropdown",
                  width := "100%",
                  marginTop := "5px",
                  title := messages("add"),
                  s"${messages("addANewStep")} ",
                  span(className := "caret")
                ),
                ul(
                  className := "dropdown-menu",
                  role := "menu",
                  marginTop := "6px",
                  minWidth := "120px",
                  AllCp.cps.toVdomArray { c =>
                    li(key := s"${c.nameStr}", a(s"${messages(c.nameStr)}", onClick --> FTool.addModule(c.nameStr)))
                  }
                )
              )
            )
          ),
          FileModalCp
            .Props(
              show = extraData.fileSelectShow,
              showChange = FTool.fileSelectShowChange,
              softKind = softKind,
              exampleFileName = extraData.exampleFileName,
              fileSelect = (v: String) => SyncIO { extraData.fileSelect(v) },
              request = extraData.request,
              tabs = extraData.tabs
            )
            .render,
          div(
            className := "form-group form-horizontal",
            marginTop := "20px",
            div(
              className := "actions col-sm-offset-2 col-sm-2",
              button(`type` := "submit", className := "btn btn-primary", width := "90%", messages("run"))
            )
          )
        )
      )
    )

  }

}
